package com.xhj.mybatis.scripting.xmltags;

import com.xhj.mybatis.reflection.Reflector;
import ognl.MemberAccess;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Member;
import java.util.Map;

/**
 * OgnlMemberAccess
 *
 * @author XJks
 * @description OgnlMemberAccess 类用于控制 OGNL 表达式访问 Java 成员（字段、方法等）的权限，参考了 OGNL 的 DefaultMemberAccess 实现。
 */
public class OgnlMemberAccess implements MemberAccess {

    /**
     * 标记当前环境是否允许通过反射控制成员的可访问性（如设置 accessible 为 true）
     */
    private final boolean canControlMemberAccessible;

    /**
     * 构造方法，初始化 canControlMemberAccessible 字段
     */
    OgnlMemberAccess() {
        // 通过 Reflector 工具类判断当前 JVM 是否允许控制成员可访问性
        this.canControlMemberAccessible = Reflector.canControlMemberAccessible();
    }

    /**
     * 设置成员的可访问性（如字段、方法），以便 OGNL 能够访问私有成员。
     *
     * @param context      OGNL 上下文环境
     * @param target       目标对象
     * @param member       需要访问的成员（字段或方法）
     * @param propertyName 属性名
     * @return 如果成员原本不可访问，则返回 Boolean.FALSE，否则返回 null
     */
    @Override
    public Object setup(Map context, Object target, Member member, String propertyName) {
        // 声明一个变量用于保存成员的原始访问状态
        Object result = null;
        // 判断是否允许控制成员可访问性
        if (isAccessible(context, target, member, propertyName)) {
            // 将 member 强制转换为 AccessibleObject（Field、Method 都是 AccessibleObject 的子类）
            AccessibleObject accessible = (AccessibleObject) member;
            // 如果当前成员不可访问
            if (!accessible.isAccessible()) {
                // 标记原本不可访问
                result = Boolean.FALSE;
                // 设置成员为可访问（绕过 Java 的访问控制检查）
                accessible.setAccessible(true);
            }
        }
        // 返回原始访问状态（用于后续恢复）
        return result;
    }

    /**
     * 恢复成员的可访问性状态。
     * 由于 accessible 标志的修改不是线程安全的，这里没有做实际恢复操作。
     *
     * @param context      OGNL 上下文环境
     * @param target       目标对象
     * @param member       需要访问的成员
     * @param propertyName 属性名
     * @param state        setup 方法返回的原始状态
     */
    @Override
    public void restore(Map context, Object target, Member member, String propertyName, Object state) {
        // todo:源码说明:由于 accessible 标志的修改不是线程安全的，这里不做恢复操作。
    }

    /**
     * 判断是否允许控制成员的可访问性。
     *
     * @param context      OGNL 上下文环境
     * @param target       目标对象
     * @param member       需要访问的成员
     * @param propertyName 属性名
     * @return 是否允许控制成员可访问性
     */
    @Override
    public boolean isAccessible(Map context, Object target, Member member, String propertyName) {
        // 直接返回 canControlMemberAccessible 字段
        return canControlMemberAccessible;
    }

}
